package com.ms.orm.mapper;

import com.alibaba.fastjson.JSON;
import com.ms.orm.constants.Constants;
import com.ms.orm.dboperator.SpringContextUtils;
import com.ms.orm.exception.BaseSqlException;
import com.ms.orm.meta.IPage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import java.lang.reflect.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Mapper接口 代理类
 * @author .
 */
public class GenericMapperFactoryBean implements FactoryBean<Object>{

    private static final Logger logger = LoggerFactory.getLogger(GenericMapperFactoryBean.class);

    private final Class<?> mapperInterface;
    private final DynamicSqlRepository dynamicSqlRepository;
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    public GenericMapperFactoryBean(Class<?> mapperInterface, DynamicSqlRepository dynamicSqlRepository, NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
        this.mapperInterface = mapperInterface;
        this.dynamicSqlRepository = dynamicSqlRepository;
        this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
    }

    @Override
    public Object getObject() throws Exception {
        if (namedParameterJdbcTemplate==null){
            //防止在bean定义注册过程中手动创建 DynamicSqlRepository 时候，namedParameterJdbcTemplate bean并没有实例化
            namedParameterJdbcTemplate = SpringContextUtils.getBean(NamedParameterJdbcTemplate.class);
        }
        return Proxy.newProxyInstance(
                getClass().getClassLoader(),
                new Class<?>[] { mapperInterface },
                (proxy, method, args) -> {
                    // 根据配置执行不同逻辑
                    return handleInvocation(method, args);
                });
    }

    private Object handleInvocation(Method method, Object[] args) {
        // 实现具体的调用逻辑
        //return null;
        String statementName = method.getName();
        SqlParser.SqlStatement sqlStatement = dynamicSqlRepository.getSqlStatementsByMapper().get(mapperInterface.getName()).get(statementName);
        if (sqlStatement == null) {
            throw new IllegalArgumentException("SQL statement not found: " + statementName);
        }

        Map<String, Object> params = dynamicSqlRepository.extractParams(args, method.getParameterAnnotations());
        //构建常用sql
        String sql = dynamicSqlRepository.buildFinalSql(sqlStatement, params);

        Class<?> returnType = method.getReturnType();
        if (returnType.equals(void.class)) {
            if(logger.isDebugEnabled()){
                logger.debug("statementName {} params {}",statementName, JSON.toJSONString(params));
            }
            namedParameterJdbcTemplate.update(sql, params);
            return null;
        } else if (List.class.isAssignableFrom(returnType)) {
            //获取方法返回值类型的 实际参数类型，比如 List<User> 中的User类型
            Type genericReturnType = method.getGenericReturnType();
            Class elementType = (Class) ((ParameterizedType) genericReturnType).getActualTypeArguments()[0];
            if(logger.isDebugEnabled()){
                logger.debug("statementName {} params {}",statementName, JSON.toJSONString(params));
            }
            if(elementType.isAssignableFrom(Map.class)) {
                return namedParameterJdbcTemplate.queryForList(sql, params);
            }else{
                return namedParameterJdbcTemplate.query(sql, params, new BeanPropertyRowMapper<>(elementType));
            }
        } else if (IPage.class.isAssignableFrom(returnType)) {
            //支持返回值是分页
            Type genericReturnType = method.getGenericReturnType();
            Class elementType = (Class) ((ParameterizedType) genericReturnType).getActualTypeArguments()[0];
            //先执行查询总数
            if(params.get(Constants.PAGE)==null){
                throw new BaseSqlException("page parameter cannot be null");
            }
            IPage page = (IPage) params.get(Constants.PAGE);
            if(page.searchCount()){
                String sqlForCount = dynamicSqlRepository.buildFinalSqlForCount(sqlStatement, params);
                if(logger.isDebugEnabled()){
                    params.remove(Constants.PAGE);
                    logger.debug("statementName {} buildFinalSqlForCount params {}",statementName, JSON.toJSONString(params));
                }
                Long totalCount = namedParameterJdbcTemplate.queryForObject(sqlForCount,params,Long.class);
                page.setTotal(totalCount);
            }
            if(logger.isDebugEnabled()){
                params.remove(Constants.PAGE);
                logger.debug("statementName {} params {}",statementName, JSON.toJSONString(params));
            }
            List<?> recordList = namedParameterJdbcTemplate.query(sql, params, new BeanPropertyRowMapper<>(elementType));
            page.setRecords(recordList!=null?recordList: Collections.emptyList());
            return page;
        } else {
            return namedParameterJdbcTemplate.queryForObject(sql, params, new BeanPropertyRowMapper<>(returnType));
        }
    }

    @Override
    public Class<?> getObjectType() {
        return mapperInterface;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}
